common-excel 模块使用

模块简介

common-excel 模块基于 EasyExcel 实现 Excel 的读写。

EasyExcel 是一个基于 Java 的简单、省内存的读写 Excel 的开源项目。在尽可能节约内存的情况下支持读写百 M 的 Excel。64M 内存 1 分钟内读取 75M (46W 行 25 列) 的 Excel,当然还有急速模式能更快,但是内存占用会在 100M 多一点。

EasyExcel 性能对比
参考示例

以下代码可以参考用户管理模块的导入导出功能

引入依赖

<!-- excel 导入导出 -->
<dependency>
  <groupId>com.pig4cloud</groupId>
  <artifactId>pigx-common-excel</artifactId>
</dependency>

定义导入导出实体

EasyExcel 支持丰富的注解来定义 Excel 字段,详细文档请参考: https://www.yuque.com/easyexcel/doc/write

public class UserExcelVO {

	@ExcelProperty("用户编号")
	private Integer userId;

	@NotBlank(message = "用户名不能为空")
	@ExcelProperty("用户名")
	private String username;
}

导出功能

后端代码

Controller 接口直接返回 List 元素即可,需要在接口上声明 @ResponseExcel 注解:

@ResponseExcel
@GetMapping("/export")
public List<UserExcelVO> export(UserDTO userDTO) {
  return userService.listUser(userDTO);
}
注解必须添加

必须在导出接口上添加 @ResponseExcel 注解,否则无法正确导出 Excel 文件

前端页面

定义 menuLeft 的插槽:

<template slot="menuLeft">
  <el-button class="filter-item" plain type="primary" size="small" icon="el-icon-download" @click="exportExcel">导出</el-button>
</template>

具体的导出事件定义, downBlobFile 已经定义成全局方法,可以直接引用:

exportExcel() {
  this.downBlobFile("/admin/user/export", this.searchForm, "user.xlsx");
}

downBlobFile 方法参数说明:

参数示例描述
url/admin/user/export请求后端的接口地址
paramsthis.searchForm查询参数
filenameuser.xlsx下载后显示的文件名称

导入功能

后端代码

Controller 接口使用 @RequestExcel 标记前端 Excel 对应的实体列表, BindingResult 用来校验实体 JSR303 校验失败结果:

@PostMapping("/import")
public R importUser(@RequestExcel List<UserExcelVO> excelVOList, BindingResult bindingResult) {
  return userService.importUser(excelVOList, bindingResult);
}

获取校验结果,把不合法数据返回前端回显原因。其中包括两种不合法(JSR303 不合法数据、自定义校验不合法数据):

List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();

JSR303 通用校验结果示例:

JSR303 校验结果

个性化校验结果示例:

自定义校验结果

校验不通过时返回错误信息:

if (CollUtil.isNotEmpty(errorMessageList)) {
  return R.failed(errorMessageList);
}

前端页面

定义 menuLeft 的插槽:

<template slot="menuLeft">
  <el-button class="filter-item" plain type="primary" size="small" icon="el-icon-upload" @click="$refs.excelUpload.show()">导入 </el-button>
</template>

引入组件 ExcelUpload:

import ExcelUpload from "/@/components/upload/excel";
<!--excel 模板导入 -->
<excel-upload
  ref="excelUpload"
  title="用户信息导入"
  url="/admin/user/import"
  temp-url="/admin/sys-file/excel-temp/user.xlsx"
  @refreshDataList="refreshChange"
></excel-upload>

excel-upload 组件参数说明:

参数示例描述
refexcelUpload固定值,和上文导入按钮的 ref 绑定值保持一致,不需要修改
title用户信息导入弹出框标题
url/admin/user/import后台接口地址
temp-url/admin/sys-file/excel-temp/user.xlsx导入模板下载地址,都是通过此接口下载,模板放在 upms 模块
Excel 模板位置

特别说明

导出枚举、字典如何处理

在导出 Excel 时,经常需要将数据库中的代码值转换为可读的显示值。例如:

  • 数据库中存储 0/1,但 Excel 中需要显示为 /
  • 数据库中存储状态码 1/2/3,但 Excel 中需要显示为 待审核/已通过/已拒绝

解决方案

虽然 EasyExcel 提供了自定义 Converter 的方式实现自动转换,但这种方式需要复杂的注解配置,反而使简单问题复杂化。

推荐做法: 创建专门用于 Excel 导出的 DTO 对象,在转换过程中手动处理枚举映射关系。

示例代码

数据库实体类:

@Data
public class User {
    private Long id;
    private String username;
    private Integer gender;  // 数据库存储: 0-女,1-男
    private Integer status;  // 数据库存储: 1-启用,0-禁用
}

Excel 导出专用 DTO:

@Data
public class UserExcelDTO {
    @ExcelProperty("用户ID")
    private Long id;

    @ExcelProperty("用户名")
    private String username;

    @ExcelProperty("性别")
    private String genderName;  // Excel中显示: 男/女

    @ExcelProperty("状态")
    private String statusName;  // Excel中显示: 启用/禁用
}

转换逻辑:

public List<UserExcelDTO> convertToExcelDTO(List<User> users) {
    return users.stream()
        .map(user -> {
            UserExcelDTO dto = new UserExcelDTO();
            dto.setId(user.getId());
            dto.setUsername(user.getUsername());

            // 性别转换
            dto.setGenderName(user.getGender() == 1 ? "男" : "女");

            // 状态转换
            dto.setStatusName(user.getStatus() == 1 ? "启用" : "禁用");

            return dto;
        })
        .collect(Collectors.toList());
}
最佳实践

使用独立的 DTO 对象进行数据转换,代码更清晰易维护,避免复杂的注解配置

导出 Excel 文件损坏

当下载的 Excel 模板无法打开,提示文件损坏时,需要在 Maven 中配置 Excel 文件过滤:

<build>
	<resources>
		<resource>
			<directory>src/main/resources</directory>
			<filtering>true</filtering>
			<excludes>
				<exclude>**/*.xlsx</exclude>
				<exclude>**/*.xls</exclude>
			</excludes>
		</resource>
		<resource>
			<directory>src/main/resources</directory>
			<filtering>false</filtering>
			<includes>
				<include>**/*.xlsx</include>
				<include>**/*.xls</include>
			</includes>
		</resource>
	</resources>
</build>
文件损坏原因

Maven 的资源过滤功能会破坏 Excel 文件的二进制格式,需要配置 filtering=false 排除 Excel 文件